package com.github.xzb617.adm.service.impl;

import com.github.xzb617.adm.dao.FlowMetricMapper;
import com.github.xzb617.adm.service.FlowMetricService;
import com.github.xzb617.client.flow.dto.ClientFlowMetric;
import com.github.xzb617.domain.common.TextCondition;
import com.github.xzb617.domain.entity.FlowMetric;
import com.github.xzb617.domain.vo.LineChartVO;
import com.github.xzb617.domain.vo.echarts.*;
import com.github.xzb617.utils.DateFormatUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class FlowMetricServiceImpl implements FlowMetricService {

    private final FlowMetricMapper flowMetricMapper;

    public FlowMetricServiceImpl(FlowMetricMapper flowMetricMapper) {
        this.flowMetricMapper = flowMetricMapper;
    }

    @Override
    @Transactional
    public void saveClientMetric(ClientFlowMetric clientFlowMetric) {
        FlowMetric flowMetric = new FlowMetric();
        BeanUtils.copyProperties(clientFlowMetric, flowMetric, "startTime");
        flowMetric.setStartTime(new Date(clientFlowMetric.getStartTime()));
        flowMetric.setId(UUID.randomUUID().toString());
        this.flowMetricMapper.insertSelective(flowMetric);
    }

    @Override
    public List<LineChartVO> getClientMetrics(TextCondition condition) {
        // 查询 8 分钟之内的上报数据
        List<FlowMetric> metricsInLastTenMinute = this.flowMetricMapper.selectByStartTime(8, condition.getSearchText());

        // 分组
        Map<String, List<FlowMetric>> collect = metricsInLastTenMinute.stream()
                .collect(Collectors.groupingBy(e -> e.getAppId() + "\n" + e.getInstanceId()));

        // 解析 过滤 转换
        List<LineChartVO> lineChartVOS = new ArrayList<>(collect.keySet().size());
        Set<Map.Entry<String, List<FlowMetric>>> entries = collect.entrySet();
        for (Map.Entry<String, List<FlowMetric>> entry : entries) {
            lineChartVOS.add(this.metricToChart(entry.getValue(), entry.getKey()));
        }

        return lineChartVOS;
    }

    /**
     * 构建图表数据模型
     * @param metrics 流控指标
     * @param title 标题
     * @return
     */
    private LineChartVO metricToChart(List<FlowMetric> metrics, String title) {
        LineChartVO chart = new LineChartVO();
        chart.setTitle(new Title(title, new Title.TextStyle()));
        chart.setTooltip(new Tooltip());
        chart.setLegend(new Legend(Sets.newHashSet("总访问数", "通过数", "拒绝数")));
        chart.setColor(new String[]{"red", "green", "orange"});
        chart.setxAxis(new XAxis(resolveXAxisData(metrics)));
        chart.setyAxis(new YAxis());
        chart.setSeries(resolveSeries(metrics));
        return chart;
    }

    private List<String> resolveXAxisData(List<FlowMetric> metrics) {
        return metrics.stream()
                .sorted(Comparator.comparing(FlowMetric::getStartTime))
                .filter(e -> e.getStartTime()!=null)
                .map(e -> {
                    return DateFormatUtil.formatToHourAndMinute(e.getStartTime());
                })
                .distinct()
                .collect(Collectors.toList());
    }

    private List<Series> resolveSeries(List<FlowMetric> metrics) {
        Series totalSeries = new Series("总访问数", "line");
        Series passSeries = new Series("通过数", "line");
        Series failSeries = new Series("拒绝数", "line");

        List<FlowMetric> collect = metrics.stream()
                .sorted(Comparator.comparing(FlowMetric::getStartTime))
                .filter(e -> e.getStartTime() != null)
                .collect(Collectors.toList());

        List<Long> totalSeriesData = new ArrayList<>();
        List<Long> passSeriesData = new ArrayList<>();
        List<Long> failSeriesData = new ArrayList<>();
        for (FlowMetric metric : collect) {
            totalSeriesData.add(metric.getTotalCount());
            passSeriesData.add(metric.getPassCount());
            failSeriesData.add(metric.getFailCount());
        }

        totalSeries.setData(totalSeriesData);
        passSeries.setData(passSeriesData);
        failSeries.setData(failSeriesData);

        return Lists.newArrayList(failSeries, passSeries, totalSeries);
    }
}
